#include "page_cache.h"

PageCache PageCache::_Inst;

Span *PageCache::NewSpan(size_t k)
{
    if (k > NPAGES)
    {
        void *ptr = SystemAlloc(k);
        // Span *span = new Span;
        Span *span = _spanPool.New();
        span->_page_id = (PAGE_ID)ptr >> PAGE_SHIFT;
        span->_n = k;

        // _idSpanMap[span->_page_id] = span;
        _idSpanMap.set(span->_page_id, span);

        return span;
    }
    assert(k > 0 && k < NPAGES);
    // 检查k页位置是否为空
    // 非空就直接弹出一个span
    // LOG(DEBUG, "在pagecache中k页位置检查是否存在非空span\n");
    if (!_spanLists[k].Empty())
    {
        Span *kspan = _spanLists[k].PopFront();
        // 建立id和span的映射
        for (PAGE_ID i = 0; i < kspan->_n; i++)
        {
            // _idSpanMap[kspan->_page_id + i] = kspan;
            _idSpanMap.set(kspan->_page_id + i, kspan);
        }
        return kspan;
    }
    // k页位置是空的，就对spanList进行遍历，寻找更大的来切成两个小的
    // LOG(DEBUG, "在pagecache中k页位置不存在非空span, 遍历后面的\n");
    for (size_t i = k + 1; i < NPAGES; ++i)
    {
        if (!_spanLists[i].Empty())
        {
            Span *nspan = _spanLists[i].PopFront();
            // Span* kspan = new Span;
            Span *kspan = _spanPool.New();

            kspan->_page_id = nspan->_page_id;
            kspan->_n = k;

            nspan->_page_id += k;
            nspan->_n -= k;

            _spanLists[nspan->_n].PushFront(nspan);

            // 建立nsapn的首尾和nspan的映射
            // _idSpanMap[nspan->_page_id] = nspan;
            // _idSpanMap[nspan->_n + nspan->_page_id - 1] = nspan;
            _idSpanMap.set(nspan->_page_id, nspan);
            _idSpanMap.set(nspan->_n + nspan->_page_id - 1, nspan);

            // 建立id和span的映射
            for (PAGE_ID i = 0; i < kspan->_n; i++)
            {
                // _idSpanMap[kspan->_page_id + i] = kspan;
                _idSpanMap.set(kspan->_page_id + i, kspan);
            }

            return kspan;
        }
    }

    // 遍历完spanList都空，就要向堆申请
    // LOG(DEBUG, "在pagecache中不存在非空span, 需要向堆申请内存\n");
    // Span* bigSpan = new Span;
    Span *bigSpan = _spanPool.New();
    void *ptr = SystemAlloc(NPAGES - 1);
    bigSpan->_page_id = (PAGE_ID)ptr >> PAGE_SHIFT;
    bigSpan->_n = NPAGES - 1;

    _spanLists[bigSpan->_n].PushFront(bigSpan);

    return NewSpan(k);
}

// 通过地址，映射出对应的span
Span *PageCache::MapObjictToSpan(void *obj)
{
    PAGE_ID id = (PAGE_ID)obj >> PAGE_SHIFT; // 算出对应的页号，根据页号从_idSpanMap中找相应的span

    // std::unique_lock<std::mutex> lock(_pageMtx);
    // auto ret = _idSpanMap.find(id);

    // if (ret != _idSpanMap.end())
    // {
    //     return ret->second;
    // }
    // else
    // {
    //     assert(false);
    //     return nullptr;
    // }
    auto ret = (Span *)_idSpanMap.get(id);
    assert(ret != nullptr);
    return ret;
}

//
void PageCache::ReleaseSpanTOPageCache(Span *span)
{
    // 大于128页的直接还给堆
    if (span->_n > NPAGES - 1)
    {
        size_t bytes = span->_n << PAGE_SHIFT;
        void *ptr = (void *)(span->_page_id << PAGE_SHIFT);
        SystemFree(ptr, bytes);
        // delete span;
        _spanPool.Delete(span);
    }
    // 对span前后的页进行合并，缓解内存碎片问题
    // LOG(DEBUG, "向前合并开始\n");
    while (true)
    {
        PAGE_ID prevID = span->_page_id - 1;
        // auto ret = _idSpanMap.find(prevID);
        // if (ret == _idSpanMap.end())
        // {
        //     //LOG(DEBUG, "前面没有可合并的页了，停止合并\n");
        //     break;
        // }
        auto ret = (Span *)_idSpanMap.get(prevID);
        if (ret == nullptr)
        {
            break;
        }

        // Span* prevSpan = ret->second;
        // if (prevSpan->_isuse)
        // {
        //     //LOG(DEBUG, "前面的页正在被使用，停止合并\n");
        //     break;
        // }
        Span *prevSpan = ret;
        if (prevSpan->_isuse)
        {
            // LOG(DEBUG, "前面的页正在被使用，停止合并\n");
            break;
        }

        if (prevSpan->_n + span->_n > NPAGES - 1)
        {
            // LOG(DEBUG, "页数达到上限，停止合并\n");
            break;
        }

        span->_page_id = prevSpan->_page_id;
        span->_n += prevSpan->_n;

        _spanLists[prevSpan->_n].Erase(prevSpan);
        // delete prevSpan;
        _spanPool.Delete(prevSpan);
    }

    // LOG(DEBUG, "向前合并结束，开始向后合并\n");
    while (true)
    {
        PAGE_ID nextID = span->_page_id + span->_n;
        // auto ret = _idSpanMap.find(nextID);
        // if (ret == _idSpanMap.end())
        // {
        //     //LOG(DEBUG, "后面没有可合并的页了，停止合并\n");
        //     break;
        // }
        auto ret = (Span *)_idSpanMap.get(nextID);
        if (ret == nullptr)
        {
            break;
        }

        // Span* nextSpan = ret->second;
        // if (nextSpan->_isuse)
        // {
        //    //LOG(DEBUG, "前面的页正在被使用，停止合并\n");
        //     break;
        // }
        Span *nextSpan = ret;
        if (nextSpan->_isuse)
        {
            // LOG(DEBUG, "前面的页正在被使用，停止合并\n");
            break;
        }

        if (nextSpan->_n + span->_n > NPAGES - 1)
        {
            // LOG(DEBUG, "页数达到上限，停止合并\n");
            break;
        }

        span->_n += nextSpan->_n;
        _spanLists[nextSpan->_n].Erase(nextSpan);

        // delete nextSpan;
        _spanPool.Delete(nextSpan);
    }

    // LOG(DEBUG, "合并完成，将合并后的span插入_spanList\n");
    _spanLists[span->_n].PushFront(span);
    span->_isuse = false;

    // 最后将span首尾页数和span建立映射
    // _idSpanMap[span->_page_id] = span;
    // _idSpanMap[span->_n + span->_page_id - 1] = span;
    _idSpanMap.set(span->_page_id, span);
    _idSpanMap.set(span->_page_id + span->_n - 1, span);
}